package com.hzw.saas.service.hpc.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hzw.saas.api.hpc.bo.HpcAccountProviderBo;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.RedisUtil;
import com.hzw.saas.common.util.Json;
import com.hzw.saas.common.util.SignatureUtil;
import com.hzw.saas.common.util.constants.CacheConsts;
import com.hzw.saas.service.hpc.exception.QingCloudAddUserException;
import com.hzw.saas.service.hpc.exception.QingCloudDeleteUserException;
import com.hzw.saas.service.hpc.exception.QingCloudException;
import com.hzw.saas.service.hpc.service.IQingCloudService;
import com.hzw.saas.service.hpc.service.bo.ClusterUsageBO;
import com.hzw.saas.service.hpc.util.QingCloudClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpException;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author sonam
 * @sine 2021/12/20 9:29 上午
 */
@Slf4j
@Service
public class QingCloudServiceImpl implements IQingCloudService {
    private static final String VERSION = "1";
    private static final String SIGNATURE_VERSION = "1";
    private static final String HMAC_SHA256 = "HmacSHA256";
    // 获取集群基本信息
    public static final String URI_LIST_CLUSTER = "/api/cluster/list";
    //    // 获取集群节点信息
//    public static final String URI_LIST_CLUSTER_NODES = "/api/cluster/listNodes";
//    // 获取集群作业信息
//    public static final String URI_LIST_JOB = "/api/job/list";
//    // 获取当前集群队列信息
//    public static final String URI_GET_CURRENT_QUEUE = "/api/queue/getCurrentQueue";
//    // 集群队列详情
//    public static final String URI_DESCRIBE_QUEUE = "/api/queue/describeQueue";
    // 获取当前集群用户列表
    public static final String URI_LIST_USER = "/api/user";
    // 增加集群用户
    public static final String URI_ADD_USER = "/api/user/addUser";
    // 删除集群用户
    public static final String URI_DELETE_USER = "/api/user/deleteUser";

    private QingCloudClient buildClient(String apiUri, String pubKey, String privateKey) {
        if (StrUtil.isBlank(apiUri)) {
            // ～tmp
            apiUri = "https://hpc.api.shanhe.com:443";
        }
        return new QingCloudClient(apiUri
                , pubKey
                , privateKey
                , VERSION
                , HMAC_SHA256
                , SIGNATURE_VERSION);
    }

    @Override
    public void addUser(HpcAccountProviderBo providerBo, String hpcAccount, String hpcPassword) {
        Map<String, String> params = buildTimeParams();
        Map<String, String> requestMap = buildAddUserRequest(hpcAccount, hpcPassword, providerBo.getClusterId(), providerBo.getZone());
        QingCloudClient qingCloudClient = buildClient(providerBo.getApiUri(), providerBo.getPubKey(), providerBo.getPrivateKey());
        String body;
        try {
            body = qingCloudClient.request(HttpMethod.POST, URI_ADD_USER, params, JSONUtil.toJsonStr(requestMap));
        } catch (HttpException e) {
            throw new QingCloudAddUserException("供应商添加账号失败", e);
        }
        checkResponse(parsingResponseBody(body), new QingCloudAddUserException("添加HPC用户失败"));
    }

    private Map<String, String> buildAddUserRequest(String username, String password, String clusterId, String zone) {
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("cluster_id", clusterId);
        requestBody.put("zone", zone);
        requestBody.put("mod", "700");
        requestBody.put("username", username);
        requestBody.put("password", password);
        return requestBody;
    }

    @Override
    public void deleteUser(HpcAccountProviderBo providerBo, String hpcAccount) throws QingCloudDeleteUserException {
        // 发请求删除用户
        Map<String, String> requestMap = buildDeleteUserRequest(providerBo, hpcAccount);
        Map<String, String> params = buildTimeParams();
        QingCloudClient qingCloudClient = buildClient(providerBo.getApiUri(), providerBo.getPubKey(), providerBo.getPrivateKey());
        String body;
        try {
            body = qingCloudClient.request(HttpMethod.POST, URI_DELETE_USER, params, JSONUtil.toJsonStr(requestMap));
        } catch (HttpException e) {
            throw new QingCloudDeleteUserException("供应商删除账号失败", e);
        }
        Map resultMap = parsingResponseBody(body);
        checkResponse(resultMap, new QingCloudDeleteUserException("删除HPC账号失败"));
    }

    private Map<String, String> buildDeleteUserRequest(HpcAccountProviderBo providerBo, String hpcAccount) {
        Map<String, String> requestMap = new HashMap<>();
        requestMap.put("cluster_id", providerBo.getClusterId());
        requestMap.put("username", hpcAccount);
        requestMap.put("zone", providerBo.getZone());
        return requestMap;
    }

    @Override
    public List<String> getUsers(HpcAccountProviderBo providerBo) throws QingCloudException {
        QingCloudClient qingCloudClient = buildClient(providerBo.getApiUri(),
                providerBo.getPubKey(),
                providerBo.getPrivateKey());
        String body;
        try {
            body = qingCloudClient.request(HttpMethod.GET, URI_LIST_USER, buildUserListRequest(providerBo));
        } catch (HttpException e) {
            throw new QingCloudException("供应商网络异常", e);
        }
        Map resultMap = parsingResponseBody(body);
        List<String> users = new ArrayList<>();
        Object users_set = resultMap.get("users_set");
        if (Objects.isNull(users_set) || !(users_set instanceof List)) {
            return users;
        }
        List<?> userMap = (List<?>) users_set;
        userMap.forEach(user -> {
            if (user instanceof Map) {
                Object username = ((Map<?, ?>) user).get("username");
                if (username != null) {
                    users.add(username.toString());
                }
            }
        });
        return users;
    }

    private Map<String, String> buildUserListRequest(HpcAccountProviderBo providerBo) {
        Map<String, String> requestMap = new HashMap<>();
        requestMap.put("cluster_id", providerBo.getClusterId());
        requestMap.put("zone", providerBo.getZone());
        return requestMap;
    }

    @Override
    public ClusterUsageBO getCacheClusterUsage(HpcAccountProviderBo providerBo) throws QingCloudException {
        String key = CacheConsts.KEY_CLUSTER_USAGE + providerBo.getProviderId();
        if (RedisUtil.hasKey(key)) {
            return RedisUtil.get(key);
        } else {
            try {
                ClusterUsageBO clusterUsage = getClusterUsage(providerBo);
                RedisUtil.set(key, clusterUsage, 60 * 60);
                return clusterUsage;
            }catch (Exception ex){
                log.error("获取青云负载出错", ex);
                return null;
            }
        }
    }

    @Override
    public ClusterUsageBO getClusterUsage(HpcAccountProviderBo providerBo) throws QingCloudException {
        AssertUtil.assertThrow("参数错误", Objects.isNull(providerBo) || !StrUtil.isAllNotBlank(providerBo.getClusterId(), providerBo.getZone()));
        QingCloudClient qingCloudClient = buildClient(providerBo.getApiUri(),
                providerBo.getPubKey(),
                providerBo.getPrivateKey());
        String body;
        try {
            body = qingCloudClient.request(HttpMethod.GET, URI_LIST_CLUSTER, buildClusterUsageParam(providerBo.getClusterId(), providerBo.getZone()));
        } catch (HttpException e) {
            throw new QingCloudException("供应商网络异常", e);
        }
        Map resultMap = parsingResponseBody(body);
        checkResponse(resultMap, new QingCloudException("供应商集群状态查询异常"));

        ClusterUsageBO clusterUsageBO = new ClusterUsageBO();

        Object clusterSets = resultMap.get("cluster_set");
        if (!(clusterSets instanceof List)) {
            return clusterUsageBO;
        }

        Object clusterSet = ((List<?>) clusterSets).get(0);
        if (!(clusterSet instanceof Map)) {
            return clusterUsageBO;
        }

        Object appCluster = ((Map<?, ?>) clusterSet).get("app_cluster");
        if (!(appCluster instanceof Map)) {
            return clusterUsageBO;
        }

        Object cpuMemUsage = ((Map<?, ?>) appCluster).get("cpu_mem_usage");
        if (!(cpuMemUsage instanceof Map)) {
            return clusterUsageBO;
        }

        Object cpu_conf = ((Map<?, ?>) cpuMemUsage).get("cpu_conf");
        Object cpu_used = ((Map<?, ?>) cpuMemUsage).get("cpu_used");
        Object mem_conf = ((Map<?, ?>) cpuMemUsage).get("mem_conf"); // 单位GB
        Object mem_used = ((Map<?, ?>) cpuMemUsage).get("mem_used");

        clusterUsageBO.setCpus(Integer.parseInt(cpu_conf.toString()));
        clusterUsageBO.setCpuUsedRate(NumberUtil.round(NumberUtil.div(NumberUtil.div(Double.parseDouble(cpu_used.toString()), 1000.0d), clusterUsageBO.getCpus()) * 100d, 2));
        clusterUsageBO.setMem(Integer.parseInt(mem_conf.toString()) >> 10);
        clusterUsageBO.setMemUsedRate(NumberUtil.round(NumberUtil.div(NumberUtil.div(Double.parseDouble(mem_used.toString()), 1024000.0d), clusterUsageBO.getMem()) * 100d, 2));
        clusterUsageBO.setMemUsed(NumberUtil.round(NumberUtil.div(NumberUtil.mul(clusterUsageBO.getMemUsedRate(), clusterUsageBO.getMem()), 100d), 2));
        clusterUsageBO.setCpuUsed(NumberUtil.round(NumberUtil.div(NumberUtil.mul(clusterUsageBO.getCpuUsedRate(), clusterUsageBO.getCpus()), 100d), 2));
        return clusterUsageBO;
    }

    private Map<String, String> buildClusterUsageParam(String clusterId, String zone) {
        Map<String, String> params = new HashMap<>();
        params.put("cluster_id", clusterId);
        params.put("zone", zone);
        params.put("service", "hpc");
        params.put("status", "[\"pending\", \"active\", \"stopped\", \"suspended\"]");
        params.put("verbose", "1");
        params.put("sort_key", "create_time");
        params.put("user_id", "usr-QjGJYe9b");
        params.put("cluster_type", "ehpc");
        params.put("reverse", "1");
        params.put("offset", "0");
        params.put("limit", "10");
        params.put("owner", "usr-QjGJYe9b");
        return params;
    }

    /**
     * 校验返回结果
     *
     * @param resultMap
     */
    private void checkResponse(Map<String, Object> resultMap, QingCloudException exception) {
        Object code = resultMap.get("ret_code");
        if (!Objects.equals(code, 0)) {
            throw exception;
        }
    }

    /**
     * 构造时间戳参数
     *
     * @return
     */
    private Map<String, String> buildTimeParams() {
        Map<String, String> params = new HashMap<>();
        params.put("timestamp", DateUtil.format(new Date(), SignatureUtil.TIME_PATTERN));
        return params;
    }

    private Map parsingResponseBody(String responseBody) {
        return Json.parseObject(responseBody, Map.class);
    }


}
